package com.tang.generate.service.impl


import com.baomidou.mybatisplus.core.metadata.IPage
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.github.yulichang.base.MPJBaseServiceImpl
import com.github.yulichang.query.MPJQueryWrapper
import com.tang.common.enums.FieldType
import com.tang.common.properties.sys.CoolTeaProperties
import com.tang.framework.core.domain.HttpResult
import com.tang.common.model.req.ReqId
import com.tang.common.model.req.ReqIds
import com.tang.common.model.req.ReqString
import com.tang.common.model.req.ReqStrings
import com.tang.generate.domain.GenFieldType
import com.tang.generate.domain.GenTable
import com.tang.generate.mapper.GenTableMapper
import com.tang.generate.model.ConfigVO
import com.tang.generate.model.DictFieldVO
import com.tang.generate.model.EnumVO
import com.tang.generate.model.GenVO
import com.tang.generate.model.req.ReqGenTable
import com.tang.generate.model.req.ReqGenerate
import com.tang.generate.model.req.TableAddReq
import com.tang.generate.model.res.ResGenDetail
import com.tang.generate.model.res.ResGenTableItem
import com.tang.generate.service.IGenFieldService
import com.tang.generate.service.IGenFieldTypeService
import com.tang.generate.service.IGenService
import com.tang.generate.utils.GenLocalUtils
import com.tang.generate.utils.GenUtils
import com.tang.generate.utils.VelocityUtils
import com.tang.service.admin.domain.SysDictType
import com.tang.service.admin.service.ISysDictTypeService
import com.tang.service.admin.service.ISysDictValueService
import jodd.util.StringUtil
import org.apache.commons.collections4.ListUtils
import org.apache.commons.lang3.StringUtils
import org.springframework.beans.BeanUtils
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.io.File
import java.io.FileOutputStream
import java.lang.Exception
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.stream.Collectors
import jakarta.annotation.Resource
import jakarta.servlet.http.HttpServletResponse


@Service
open class GenServiceImpl : IGenService, MPJBaseServiceImpl<GenTableMapper, GenTable>() {


    @Resource
    private lateinit var genFieldService: IGenFieldService;

    @Resource
    private lateinit var genFieldTypeService: IGenFieldTypeService;

    @Resource
    private lateinit var sysDictTypeService: ISysDictTypeService;


    @Resource
    private lateinit var sysDictValueService: ISysDictValueService;

    @Resource
    private lateinit var coolTeaProperties: CoolTeaProperties;


    override fun list(reqGenTableItem: ReqGenTable): IPage<ResGenTableItem> = selectJoinListPage(
        Page<ResGenTableItem>(reqGenTableItem.pageNum, reqGenTableItem.pageSize),
        ResGenTableItem::class.java,
        MPJQueryWrapper<GenTable>().select("t.id as id").select("t.table_name as tableName")
            .select("t.comment as comment").select("t.create_time as createTime").select("t.update_time as updateTime")
            .select("t.database_name as databaseName")
            .like(StringUtil.isNotBlank(reqGenTableItem.tableName), "t.table_name", reqGenTableItem.tableName)
    )


    override fun detail(reqId: ReqId): ResGenDetail? {
        val genDetail = selectJoinOne(
            ResGenDetail::class.java,
            MPJQueryWrapper<GenTable?>().select("t.table_name as tableName").select("t.database_name as databaseName")
                .select("t.comment as comment").select("t.class_name as className").select("t.module as module")
                .select("t.Tag as Tag").select("t.package_name as packageName").select("t.front_path as frontPath")
                .select("t.fun_name as funName").select("t.clazz_name as clazzName").select("t.class_name as className")
                .select("t.authorize as authorize")
                .select("t.module as module").select("t.id as id").eq("t.id", reqId.id)
        );
        val genFieldList = genFieldService.query().eq("table_id", genDetail.id).list();
        genDetail.genFieldList = genFieldList;
        return genDetail;
    }


    @Transactional
    override fun importTable(reqStrings: ReqStrings): HttpResult<String?> {
        if (reqStrings.commonList.isEmpty()) {
            return HttpResult.error("没有选择导入的表");
        }
        val tableList: List<Map<String, String>> = baseMapper.getTableListByTableNameList(null, reqStrings.commonList)
        tableList.forEach {
            val genTable = GenTable(
                tableName = it["TABLE_NAME"],
                className = it["TABLE_NAME"]?.let { tableName -> GenUtils.getClassName(tableName) },
                clazzName = it["TABLE_NAME"]?.let { tableName -> GenUtils.getClazzName(tableName) },
                comment = it["TABLE_COMMENT"],
                databaseName = it["TABLE_SCHEMA"],
                Tag = it["TABLE_NAME"]?.let { tableName -> GenUtils.getTag(tableName) },
                frontPath = it["TABLE_NAME"]?.let { tableName -> GenUtils.getTag(tableName) },
                packageName = it["TABLE_NAME"]?.let { tableName -> GenUtils.getPackageName(tableName) },
                module = "",
                funName = it["TABLE_NAME"]?.let { tableName -> GenUtils.getClazzName(tableName) },
            )
            save(genTable);
            genFieldService.saveGenField(genTable.id!!, genTable.tableName!!);
        }
        return HttpResult.ok();
    }

    @Transactional(rollbackFor = [Exception::class])
    override fun save(tableAddReq: TableAddReq) {
        val genTable = GenTable(
            tableName = tableAddReq.tableName,
            className = StringUtils.uncapitalize(tableAddReq.className),
            clazzName = tableAddReq.className,
            comment = tableAddReq.desc,
            databaseName = tableAddReq.databaseName,
            Tag = tableAddReq.tableName?.let { tableName -> GenUtils.getTag(tableName) },
            frontPath = tableAddReq.tableName?.let { tableName -> GenUtils.getTag(tableName) },
            packageName = tableAddReq.tableName?.let { tableName -> GenUtils.getPackageName(tableName) },
            module = "",
            funName = tableAddReq.tableName?.let { tableName -> GenUtils.getClazzName(tableName) },
            charset = tableAddReq.charset,
            charsetSort = tableAddReq.charsetSort,
            autoIncrement = tableAddReq.autoIncrement,
            engine = tableAddReq.engine
        )
        saveOrUpdate(genTable);
        genFieldService.saveGenFields(tableAddReq.fieldList!!, genTable.id!!);
    }

    override fun databaseTableList(reqGenTableItem: ReqGenTable): HttpResult<IPage<Unit>>? = HttpResult.ok(
        baseMapper.getTableList(
            Page(reqGenTableItem.pageNum, reqGenTableItem.pageSize),
            coolTeaProperties.databaseName,
            reqGenTableItem.tableName,
        )
    )

    @Transactional
    override fun editGenTable(resGenDetail: ResGenDetail): HttpResult<String?> {
        val genTable = GenTable();
        BeanUtils.copyProperties(resGenDetail, genTable);
        updateById(genTable);
        resGenDetail.genFieldList.forEach {
            genFieldService.updateById(it);
        }
        return HttpResult.ok();
    }

    override fun downloadCode(reqIds: ReqIds, httpServletResponse: HttpServletResponse) {
        val outputStream = httpServletResponse.outputStream;
        val genVoList: List<GenVO> = getGenVOList(reqIds);
        val zipCode = VelocityUtils.getZipCode(genVoList);
        httpServletResponse.reset()
        httpServletResponse.addHeader("Access-Control-Allow-Origin", "*")
        httpServletResponse.addHeader("Access-Control-Expose-Headers", "Content-Disposition")
        httpServletResponse.setHeader("Content-Disposition", "attachment; filename=\"${"cool.zip"}\"")
        httpServletResponse.addHeader("Content-Length", "" + zipCode.size)
        httpServletResponse.setContentType("application/octet-stream; charset=UTF-8")
        outputStream.write(zipCode);
        outputStream.flush();

    }

    override fun generateLocal(reqGenerate: ReqGenerate?) {
        val genVOList = getGenVOList(reqGenerate as ReqIds)
        val codeList = VelocityUtils.getPreviewCode(genVOList);
        codeList.forEach{ it ->
            it.value.forEach {
                it.forEach { (t, u) ->
                    GenLocalUtils.genCodeLocal(reqGenerate, t, u)
                }
            }
        }

    }

    private fun getGenVOList(reqIds: ReqIds): List<GenVO> {
        val genVOList = selectJoinList(
            GenVO::class.java,
            MPJQueryWrapper<GenTable?>().select("t.table_name as tableName").select("t.comment as comment")
                .select("t.class_name as className").select("t.module as module").select("t.Tag as Tag")
                .select("t.modal as modal").select("t.package_name as packageName").select("t.front_path as frontPath")
                .select("t.fun_name as funName").select("t.clazz_name as clazzName").select("t.class_name as className")
                .select("t.authorize as authorize")
                .select("t.id as id").`in`("t.id", reqIds.ids)
        )
        genVOList.forEach {
            val genFieldList = genFieldService.query().eq("table_id", it.id).list();
            it.fieldList = genFieldList;
            val addFieldList =
                it.fieldList?.stream()?.filter { item -> return@filter item.add!! }?.collect(Collectors.toList());
            it.fieldList = addFieldList;
            it.fieldList?.let { fieldList ->
                if (fieldList.size > 5) {
                    it.group = true;
                }
            }
            it.dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd: HH:mm:ss"))
            if (it.group!!) {
                it.groupFieldList = ListUtils.partition(it.fieldList, 4)
            }
        }
        return genVOList;
    }

    override fun previewCode(reqIds: ReqIds): Map<String, String> {
        val genVOList = getGenVOList(reqIds)
        return VelocityUtils.getPreviewCode(genVOList[0]);
    }

    override fun genFieldTypeList(reqString: ReqString): List<GenFieldType>? {
        return genFieldTypeService.query().like(StringUtils.isNotBlank(reqString.common), "name", reqString.common)
            .list()
    }

    override fun generateDictCode(reqGenerate: ReqGenerate?, httpServletResponse: HttpServletResponse) {
        val sysDictType = sysDictTypeService.getById(reqGenerate!!.id)
        val clazzName = GenUtils.getClazzName(sysDictType.name);
        if (reqGenerate.download!!) {
            val outputStream = httpServletResponse.outputStream;
            val zipCode = if (reqGenerate.enum_!!) {
                VelocityUtils.getZipEnumCode(getEnumVO(sysDictType, reqGenerate), reqGenerate.groupId!!);
            } else {
                VelocityUtils.getZipConfigCode(getConfigVO(sysDictType, reqGenerate), reqGenerate.groupId!!);
            }
            httpServletResponse.reset()
            httpServletResponse.addHeader("Access-Control-Allow-Origin", "*")
            httpServletResponse.addHeader("Access-Control-Expose-Headers", "Content-Disposition")
            if (reqGenerate.enum_!!) {
                httpServletResponse.setHeader("Content-Disposition", "attachment; filename=\"${"cool-enum.zip"}\"")
            } else {
                httpServletResponse.setHeader("Content-Disposition", "attachment; filename=\"${"cool-config.zip"}\"")
            }
            httpServletResponse.addHeader("Content-Length", "" + zipCode.size)
            httpServletResponse.setContentType("application/octet-stream; charset=UTF-8")
            outputStream.write(zipCode);
            outputStream.flush();
        } else {
            val groupIdPath = reqGenerate.groupId?.replace(".", "/");
            val file: File = if (reqGenerate.enum_!!) {
                File("${System.getProperty("user.dir")}/common/src/main/java/${groupIdPath}/common/enums/${clazzName}.java")
            } else {
                File("${System.getProperty("user.dir")}/common/src/main/java/${groupIdPath}/common/properties/${clazzName}Properties.java")
            }
            file.createNewFile();
            val fileOutputStream = FileOutputStream(file)
            if (reqGenerate.enum_!!) {
                fileOutputStream.write(VelocityUtils.getCodeByEnum(getEnumVO(sysDictType, reqGenerate)).toByteArray());
            } else {
                fileOutputStream.write(
                    VelocityUtils.getCodeByConfig(getConfigVO(sysDictType, reqGenerate)).toByteArray()
                )
            }
            fileOutputStream.flush();
            fileOutputStream.close();
        }
    }

    private fun getConfigVO(sysDictType: SysDictType, reqGenerate: ReqGenerate?): ConfigVO {
        val configVO = ConfigVO();
        configVO.author = coolTeaProperties.author;
        configVO.desc = sysDictType.desc;
        configVO.name = sysDictType.name;
        configVO.groupIdPath = reqGenerate?.groupId;
        configVO.dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        configVO.clazzName = GenUtils.getClazzName(sysDictType.name);
        val sysDictValueList = sysDictValueService.query().eq("dict_type_id", reqGenerate?.id).list()
        val configFieldList = sysDictValueList.stream().filter { return@filter it.status }.map {
            val dictFieldVO = DictFieldVO();
            dictFieldVO.name = GenUtils.getClassName(it.label.lowercase(Locale.getDefault()));
//            dictFieldVO.value = Integer.valueOf(it.value);
            val importByName = genFieldTypeService.getImportByName(it.type)
            if (StringUtils.isNotBlank(importByName)) {
                configVO.importList?.add(importByName);
            }
            dictFieldVO.fieldType = it.type;
            dictFieldVO.annotationName = it.label;
            dictFieldVO.desc = it.desc;
            return@map dictFieldVO
        }.collect(Collectors.toList())
        configVO.fieldList = configFieldList;
        return configVO;
    }

    private fun getEnumVO(sysDictType: SysDictType, reqGenerate: ReqGenerate?): EnumVO {
        val enumVO = EnumVO();
        enumVO.author = coolTeaProperties.author;
        enumVO.desc = sysDictType.desc;
        enumVO.dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        enumVO.clazzName = GenUtils.getClazzName(sysDictType.name);
        val sysDictValueList = sysDictValueService.query().eq("dict_type_id", reqGenerate?.id).list()
        val enumFieldList = sysDictValueList.stream().filter { return@filter it.status }.map {
            val dictFieldVO = DictFieldVO();
            dictFieldVO.name = it.label;
            dictFieldVO.value = Integer.valueOf(it.value);
            dictFieldVO.desc = it.desc;
            return@map dictFieldVO
        }.collect(Collectors.toList())
        enumVO.fieldList = enumFieldList;
        return enumVO
    }


    override fun createEnum(reqId: ReqId, httpServletResponse: HttpServletResponse?) {
        val sysDictType = sysDictTypeService.getById(reqId.id!!);
        val clazzName = GenUtils.getClazzName(sysDictType.name);
        var genFieldType = genFieldTypeService.query().eq("name", clazzName).one()
        if (Objects.isNull(genFieldType)) {
            genFieldType = GenFieldType();
        }
        genFieldType.name = clazzName;
        genFieldType.type = FieldType.CUSTOMIZE;
        genFieldType.importStr = "import ${coolTeaProperties.defaultEnumsPackage}.${clazzName};"
        genFieldTypeService.saveOrUpdate(genFieldType);
    }

    @Transactional
    override fun syncTable(reqStrings: ReqStrings): HttpResult<String> {
        reqStrings.commonList.forEach {
            val table: Map<String, String> = baseMapper.getTableListByTableNameList(null, reqStrings.commonList)[0]
            val genTable = getGenTable(it);
            genTable.tableName = table["TABLE_NAME"];
            genTable.className = table["TABLE_NAME"]?.let { tableName -> GenUtils.getClassName(tableName) };
            genTable.clazzName = table["TABLE_NAME"]?.let { tableName -> GenUtils.getClazzName(tableName) };
            genTable.comment = table["TABLE_COMMENT"];
            genTable.databaseName = table["TABLE_SCHEMA"];
            genTable.Tag = table["TABLE_NAME"]?.let { tableName -> GenUtils.getTag(tableName) };
            genTable.frontPath = table["TABLE_NAME"]?.let { tableName -> GenUtils.getTag(tableName) };
            genTable.packageName = table["TABLE_NAME"]?.let { tableName -> GenUtils.getPackageName(tableName) };
            genTable.module = "";
            genTable.funName = table["TABLE_NAME"]?.let { tableName -> GenUtils.getClazzName(tableName) };
            saveOrUpdate(genTable);
            genFieldService.saveGenField(genTable.id!!, genTable.tableName!!);
        }
        return HttpResult.ok();
    }


    private fun getGenTable(tableName: String): GenTable = query().eq("table_name", tableName).one();
}
